#ifndef EXPRESSION_H
#define EXPRESSION_H

#include "Core/Variational/var.h"
#include <queue>
#include <unordered_map>
#include <unordered_set>

namespace QPanda {
namespace Variational {

/**
 * @brief A proxy class for the Variational::var
 * @ingroup Variational
 */
class expression {
public:

    /**
     * @brief Construct a new expression object
     */
    expression() {};

    /**
     * @brief Construct a new expression object
     * 
     * @param root the root variable, denoting the y in dy/dx.
     */
    expression(var root);

    /**
     * @brief Get the root
     * 
     * @return var the root, denoting the y in dy/dx.
     */
    var getRoot() const;

    /**
     * @brief recursively finding the leaves for the root
     * 
     * @return std::vector<var> the vector of the leaves
     */
    std::vector<var> findLeaves();

    /**
     * @brief feed forward the expression, return the value
     * 
     * @return MatrixXd The result
     */
    MatrixXd propagate();
    
    /**
     * @brief feed forward the expression with given leaves
     * 
     * @param leaves leaves either generated by expression::findLeaves
     * or assigned by user (usually not including the training data
     * placeholders).
     * 
     * @return MatrixXd The reuslts.
     */
    MatrixXd propagate(const std::vector<var>& leaves);

    /**
     * @brief find all non-constants variable for the given leaves.
     * if there is some variable not related in the path from the
     * root to the leaves, it will not be added into the set
     * 
     * @return std::unordered_set<var> All non-constants variables.
     */
    std::unordered_set<var> findNonConsts(const std::vector<var>&);    
    std::unordered_set<var> findNonConsts(const std::unordered_set<var>&);
    
    /**
     * @brief backpropagation and evalute all gradients.
     * 
     * @param leaves The var-grad map. This function will update the
     * gradients.
     */
    void backpropagate(std::unordered_map<var, MatrixXd>& leaves);

    /**
     * @brief backpropagation and evalute all gradients.
     * 
     * @param leaves The var-grad map. This function will update the
     * gradients.
     * @param nonconsts all non-constants. using this to remove unnecessary
     * nodes from the gradient-evaluation.
     */
    void backpropagate(std::unordered_map<var, MatrixXd>& leaves,
     const std::unordered_set<var>& nonconsts);
    
	std::unordered_set<var> findVariables();

private:
    var root;
};

}
}

#endif // ! EXPRESSION_H